import itertools
import json
import time
import random
from multiprocessing import Pool, cpu_count
from so2 import lp_problem

class Solution:
    # 全天的系数 目标值
    default_max_all = [1.05,  1.2, 1.2, 1.2]
    default_min_all = [0.95, 0.95, 0.95, 0.95]
    # 早餐能量占比系数
    # 不参与线性规划 最小值为直接下限
    default_max1 = [0.3, 0.4, 0.4, 0.4]
    default_min1 = [0.2, 0.2, 0.2, 0.2]
    # 午餐能量占比系数
    # 参与线性规划 不设最小值
    default_max2 = [0.4, 0.6, 0.6, 0.6]
    default_min2 = [0, 0, 0, 0]
    # 晚餐能量占比系数
    # 参与线性规划 不设最小值
    default_max3 = [0.3, 0.4, 0.4, 0.4]
    default_min3 = [0, 0, 0, 0]

    def __init__(self, min_all=default_min_all, max_all=default_max_all, min1=default_min1, max1=default_max1,
                min2=default_min2, max2=default_max2, min3=default_min3, max3=default_max3):
        self.min_all = min_all

        self.max_all = max_all
        self.min1 = min1
        self.max1 = max1
        self.min2 = min2
        self.max2 = max2
        self.min2 = min2
        self.max3 = max3
        self.min3 = min3

    def handler(self, first):
        itemContent = first['itemContent']

        a = itemContent.get('早餐')['recipes_items']
        b = itemContent.get('午餐')['recipes_items']
        c = itemContent.get('晚餐')['recipes_items']

        o = first.get('energy')

        a = self.data_correct(a)
        b = self.data_correct(b)
        c = self.data_correct(c)

        a = self.deta_filer(a)
        print(a)
        b = self.deta_filer(b)
        c = self.deta_filer(c)

        print(o)
        calorie = o.get('calorie')
        protein = calorie * 0.133 / 4
        fat = calorie * 0.267 / 9
        carbohydrate = calorie * 0.6 / 4

        target = {
            'calorie': calorie,
            'protein': protein,
            'fat': fat,
            'carbohydrate': carbohydrate
        }

        result = self.find_target(a, b, c, self.min_all, self.max_all, target)
        if result is None:
            return None
        else:
            a, b, c, now, ratio, r = result

            print(r)
            for i in range(len(r)):
                if i < len(a):
                    a[i]['ratio'] = r[i]
                elif i < len(b) + len(a):
                    b[i - len(a)]['ratio'] = r[i]
                else:
                    c[i - len(a) - len(b)]['ratio'] = r[i]

            return {
                'day': first.get('day'),
                'breakfast': list(a),
                'lunch': list(b),
                'dinner': list(c),
                'target': target,
                'result': now,
                'result_ratio': ratio
            }

    def expand(self, item, r):
        items = []
        for obj in item:
            new_obj = obj.copy()
            new_obj['Calorie'] = obj['Calorie'] * r
            new_obj['Protein'] = obj['Protein'] * r
            new_obj['Fat'] = obj['Fat'] * r
            new_obj['Carbohydrate'] = obj['Carbohydrate'] * r
            new_obj['Per_Edible'] = obj['Per_Edible'] * r
            items.append(new_obj)
        return items
    def read_json(self, path):
        with open(path, 'r', encoding='utf-8') as f:
            content = json.load(f)
            return content


    def write_json(self, result_arr, path):
        with open(path, 'w', encoding='utf-8') as file:
            json.dump(result_arr, file, ensure_ascii=False, indent=4)
            print('已输出结论')

    def find_min(self, arr):
        arr2 = list(map(lambda d: d['Calorie'], arr))
        i = arr2.index(min(arr2))
        print(arr[i])
        return arr[i]

    # 过滤菜品，过滤掉单菜超上限的菜
    def dish_filter(self, arr, max, target):
        a1 = []
        a2 = []
        for key, value in arr.items():
            obj = {'name': key}
            # setattr(obj, 'name', key)
            obj.update(value)
            a2.append(obj)
            if obj['Calorie'] < target['calorie'] * max[0] and obj['Protein'] < target['protein'] * max[1] and obj[
                'Fat'] < target['fat'] * max[2] and obj['Carbohydrate'] < target['carbohydrate'] * max[3]:
                a1.append(obj)

        if len(a1) == 0:
            item = self.find_min(a2)
            a1.append(item)

        return a1, a2

    # 二次过滤 早中晚的能量占比 筛选
    def result_filter(self, arr, min, max, target):
        result_a = []
        for a in arr:
            calorie_a = 0
            protein_a = 0
            fat_a = 0
            carbohydrate_a = 0
            for a_1 in a:
                calorie_a += a_1['Calorie']
                protein_a += a_1['Protein']
                fat_a += a_1['Fat']
                carbohydrate_a += a_1['Carbohydrate']
            if target['calorie'] * min[0] <= calorie_a < target['calorie'] * max[0] and target['protein'] * min[
                1] <= protein_a < target['protein'] * max[1] and target['fat'] * min[2] <= fat_a < target['fat'] * \
                    max[
                        2] and target['carbohydrate'] * min[3] <= carbohydrate_a < target['carbohydrate'] * max[3]:
                result_a.append(a)

        return result_a

    @staticmethod
    def _judge2(args):
        instance, result_a, result_b, result_c, min_all, max_all, target = args
        count = 0

        # min_item = {
        #     "a": None,
        #     'b': None,
        #     'c': None,
        #     'now': None,
        #     'ratio': None,
        #     'r': None
        # }
        # min_ratio = 0

        for a in result_a:
            for b in result_b:
                for c in result_c:
                    count += 1

                    r = lp_problem(a, b, c, target)
                    if r is None:
                        continue
                    else:
                        now, ratio = instance.count_power(a, b, c, target, r)
                        return a, b, c, now, ratio, r, 'target', count
        return None

    # 穷举系数
    def count_ratio(self, length):
        ratios = [1, 1.25, 1.5, 2, 2.5, 3]
        # ratios = [1]
        return list(itertools.product(ratios, repeat=length))

    def find_target(self, result_a, result_b, result_c, min_all, max_all, target):
        i = 0
        count = 0

        min_item = {
            "a": None,
            'b': None,
            'c': None,
            'now': None,
            'ratio': None,
            'r': None
        }
        min_ratio = 0

        result = self._judge2((self, result_a, result_b, result_c, min_all, max_all, target))
        if result is not None:
            a, b, c, now, ratio, r, t, _count = result
            print(_count)
            count += _count
            print(count)
            return a, b, c, now, ratio, r

        print(count)
        if min_ratio == 0:
            return None
        else:
            return min_item['a'], min_item['b'], min_item['c'], min_item['now'], min_item['ratio'], min_item[
                'r']

    def data_correct(self, list):
        for i, c in enumerate(list):
            for item in list[i]:
                r = 100 / item['Per_Edible']
                item['Calorie'] = item['Calorie'] * r
                item['Protein'] = item['Protein'] * r
                item['Fat'] = item['Fat'] * r
                item['Carbohydrate'] = item['Carbohydrate'] * r
                item['Per_Edible'] = 100
        return list

    def deta_filer(self, list):
        result = []

        for i,c in enumerate(list):
            count = 0
            # foods = []
            flag = False
            for item in list[i]:
                if item['solid_liquid'] == "液体":
                    count += 1
                if count > 1:
                    break
                # f = False
                # for food in item['Ingredients']:
                #     if food in foods:
                #         flag = True
                #         f = True
                #         break
                #     else:
                #         foods.append(food)
                # if f:
                #     break
            if not flag and count < 2:
                result.append(list[i])
        return result

    def data_correct_eat(self, obj):
        for item in obj:
            r = 100 / item['Per_Edible']
            item['Calorie'] = item['Calorie'] * r
            item['Protein'] = item['Protein'] * r
            item['Fat'] = item['Fat'] * r
            item['Carbohydrate'] = item['Carbohydrate'] * r



    def count_power(self, a, b, c, target, r):
        now = {
            'calorie': 0,
            'protein': 0,
            'fat': 0,
            'carbohydrate': 0
        }
        for i_a, item_a in enumerate(a):
            now['calorie'] += item_a['Calorie'] * r[i_a]
            now['protein'] += item_a['Protein'] * r[i_a]
            now['fat'] += item_a['Fat'] * r[i_a]
            now['carbohydrate'] += item_a['Carbohydrate'] * r[i_a]
        for i_b, item_b in enumerate(b):
            idx = i_b + len(a)
            now['calorie'] += item_b['Calorie'] * r[idx]
            now['protein'] += item_b['Protein'] * r[idx]
            now['fat'] += item_b['Fat'] * r[idx]
            now['carbohydrate'] += item_b['Carbohydrate'] * r[idx]
        for i_c, item_c in enumerate(c):
            idx = i_c + len(a) + len(b)
            now['calorie'] += item_c['Calorie'] * r[idx]
            now['protein'] += item_c['Protein'] * r[idx]
            now['fat'] += item_c['Fat'] * r[idx]
            now['carbohydrate'] += item_c['Carbohydrate'] * r[idx]

        ratio = {
            'calorie': now['calorie'] / target['calorie'],
            'protein': now['protein'] / target['protein'],
            'fat': now['fat'] / target['fat'],
            'carbohydrate': now['carbohydrate'] / target['carbohydrate']
        }
        return now, ratio


if __name__ == '__main__':
    s = Solution()
    content = s.read_json('data_info20.json')
    result_arr = []

    start_time = time.time()
    first = content[0]
    result = s.handler(first)
    print(result)

    s.write_json(result_arr, 'log.json')
    end_time = time.time()
    print(end_time - start_time)
    print(result_arr)
